// ..\MCHdTreeCtrl\CMCTreeCtrl.cpp : implementation file
//

#include "stdafx.h"
#include "commctrl.h"
#include "..\MCHdTree\CMCTreeCtrl.h"
#include "..\MCHdTree\CInPlcEdit.h"
#include ".\cmctreectrl.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

// CMCTreeCtrl

IMPLEMENT_DYNAMIC(CMCTreeCtrl, CTreeCtrl)

//
// CMCTreeCtrl message handlers
//
BEGIN_MESSAGE_MAP(CMCTreeCtrl, CTreeCtrl)
	ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnNMCustomdraw)
	ON_WM_LBUTTONDOWN()
	ON_WM_LBUTTONUP()
	ON_EN_KILLFOCUS(CMCT_EDIT, OnEnKillfocus)
	ON_NOTIFY_REFLECT(TVN_DELETEITEM, OnTvnDeleteitem)
END_MESSAGE_MAP()

//
// contructor
//
CMCTreeCtrl::CMCTreeCtrl()
	:m_dwDimension(0),
	 m_dwWidth(0),m_vcClmn(0),m_iXoff(0), 
	 m_pcEdit(NULL)
{
}

CMCTreeCtrl::~CMCTreeCtrl()
{
}

//!!!must first call this function, and can only call once!!!
BOOL CMCTreeCtrl::SetDimension(DWORD dwDimension)
{
	if(0!=m_dwDimension)
	{
		TRACE("SetDimension warning org dimesion is:%d\r\n", m_dwDimension);
		return FALSE;
	}

	m_dwDimension=dwDimension;
	return TRUE;
}

//
// we use TVN_DELETEITEM to do some deletings. So parent can't recivieve this message.
// instead this control send a new notify MCTVN_DELETEITEM to parent
//
void CMCTreeCtrl::OnTvnDeleteitem(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
	// TODO: Add your control notification handler code here

	// change the para part, send MCTVN_DELETEITEM notify to parent
	CMCTreeItem* pMCTItem=reinterpret_cast<CMCTreeItem*>(pNMTreeView->itemOld.lParam);
	ASSERT(NULL!=pMCTItem);
	pNMHDR->code=MCTVN_DELETEITEM;
	pNMTreeView->itemOld.lParam=pMCTItem->m_dwPara;
	if(::SendMessage(::GetParent(m_hWnd), WM_NOTIFY, GetDlgCtrlID(), reinterpret_cast<LPARAM>(pNMHDR)))
	{
		pNMTreeView->itemOld.lParam=reinterpret_cast<LPARAM>(pMCTItem);
		return;
	}
        
	delete pMCTItem;

	*pResult = 0;
	return ;
}

//
//  use presubclass(not OnCreate or PrevCreate) to modify some property,
//  we may not receive WM_CREATE for a dialog.
//
void CMCTreeCtrl::PreSubclassWindow()
{
	// TODO: Add your specialized code here and/or call the base class

	// modify the window style
	DWORD dwStyle=GetWindowLong(m_hWnd, GWL_STYLE);
	dwStyle|=(TVS_NOHSCROLL);
	SetWindowLong(m_hWnd, GWL_STYLE, dwStyle);

	CTreeCtrl::PreSubclassWindow();
}

//
// filter the mouse message for the tree control
//
// these two message handle is called in function "CWnd::WindowProc", 
// so need to modify the parameter for each function

BOOL CMCTreeCtrl::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
	// TODO: Add your specialized code here and/or call the base class

	//
	// do some filter here, 
	// we can't filter WM_NOTIFY, for it is send from treectrl to parent
	//

	// modify the mouse message's coordinate, 
	// before the message is passed to the message map of MFC firmwork
	if( (WM_MOUSELAST>=message) && (WM_MOUSEFIRST<=message) )
	{
		lParam-=m_iXoff;
	}

	// update sub controls (edit control)
	if( (WM_MOUSEWHEEL==message) || (WM_SIZE==message) || (WM_VSCROLL==message) )
	{
		UpdateSubCtrls();
	}

	return CTreeCtrl::OnWndMsg(message, wParam, lParam, pResult);
}

LRESULT CMCTreeCtrl::DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
	// TODO: Add your specialized code here and/or call the base class
	// filter and modify the mouse message's coordinate, 
	// after the message is passed through the message map of MFC firmwork, 
	// and before it is send to the default handle
	if( (WM_MOUSELAST>=message) && (WM_MOUSEFIRST<=message) )
	{
		lParam-=m_iXoff;
	}

	return CTreeCtrl::DefWindowProc(message, wParam, lParam);
}

//
// draw mctree
//
void CMCTreeCtrl::OnNMCustomdraw(NMHDR *pNMHDR, LRESULT *pResult)
{
	// no column just as normal
	if(0==m_vcClmn.size())
	{
		*pResult = CDRF_DODEFAULT;
		return;
	}

	NMCUSTOMDRAW* pNMCustomDraw = reinterpret_cast<NMCUSTOMDRAW*>(pNMHDR);
	NMTVCUSTOMDRAW* pNMTVCustomDraw = reinterpret_cast<NMTVCUSTOMDRAW*>(pNMHDR);
	switch (pNMCustomDraw->dwDrawStage)
	{
	case CDDS_PREPAINT:
		SetViewportOrgEx(pNMCustomDraw->hdc, m_iXoff, 0, NULL);
		*pResult = CDRF_NOTIFYITEMDRAW;
		break;

	case CDDS_ITEMPREPAINT:
		*pResult = CDRF_DODEFAULT | CDRF_NOTIFYPOSTPAINT;
		break;

	case CDDS_ITEMPOSTPAINT:
		{
			if (((CRect)(pNMCustomDraw->rc)).IsRectEmpty())
			{ // nothing to paint
				*pResult = CDRF_DODEFAULT;
				break;
			}

			HTREEITEM hItem = (HTREEITEM)pNMCustomDraw->dwItemSpec;
			CMCTreeItem* pItem= reinterpret_cast<CMCTreeItem*>(CTreeCtrl::GetItemData(hItem));
			CDC dc;
			dc.Attach(pNMCustomDraw->hdc);

			CRect rcItem, rcSubItem, rcText;
			GetItemRect(hItem, &rcItem, TRUE);

			//clear the original label rectangle exceed the column0 width
			if(rcItem.right> m_vcClmn[0].m_wWidth)
			{
				rcItem.left=m_vcClmn[0].m_wWidth;
				dc.FillSolidRect(&rcItem, pNMTVCustomDraw->clrTextBk);
			}

			// fill background
			rcItem.left=rcItem.right;
			rcItem.right=m_dwWidth;
			dc.FillSolidRect(&rcItem, pNMTVCustomDraw->clrTextBk);

			//draw column0's edge
			rcSubItem=rcItem;
			rcSubItem.left=m_iXoff;
			rcSubItem.right=m_vcClmn[0].m_wWidth;
			dc.DrawEdge(&rcSubItem, BDR_SUNKENINNER, BF_RIGHT|BF_BOTTOM);

			rcSubItem=rcItem;
			rcSubItem.left=m_vcClmn[0].m_wWidth;
			// draw subitems
			for (DWORD i=1; i<m_vcClmn.size(); 
				rcSubItem.left=rcSubItem.right, i++ )
			{
				rcSubItem.right=rcSubItem.left+m_vcClmn[i].m_wWidth;
				rcText=rcSubItem;
				rcText.DeflateRect(9, 1, 2, 1);
				COLORREF clrTxtOld=dc.SetTextColor(pNMTVCustomDraw->clrText);
				COLORREF clrBkOld=dc.SetBkColor(pNMTVCustomDraw->clrTextBk);
				dc.DrawText(pItem->m_vStr[i], &rcText, DT_NOPREFIX);
				dc.SetTextColor(clrTxtOld);
				dc.SetBkColor(clrBkOld);
				dc.DrawEdge(&rcSubItem, BDR_SUNKENINNER, BF_RIGHT|BF_BOTTOM);
			}

			dc.Detach();
		}
		*pResult = CDRF_DODEFAULT;
		break;

	default:
		*pResult = CDRF_DODEFAULT;
	}
}

//
// hscroll, but no hscroll bar
//
void CMCTreeCtrl::GetHPosInfo(DWORD* pdwCurPos, DWORD* pdwMax)
{
	*pdwCurPos=-m_iXoff;

	if(NULL!=pdwMax)
	{
		*pdwMax=m_dwWidth;
	}
}
BOOL CMCTreeCtrl::SetHPos(DWORD dwPos)
{
	RECT rc;
	GetClientRect(&rc);
	if(dwPos>m_dwWidth-rc.right)
	{
		return FALSE;
	}

	ScrollWindow(-((int)(dwPos)+m_iXoff), 0, &rc, &rc);
	m_iXoff=-dwPos;
	UpdateSubCtrls();
	return TRUE;
}


//
// Hit test
//
HTREEITEM CMCTreeCtrl::HitTest(CPoint pt, UINT* pFlags, DWORD* pdwSubId)
{
	// this is not mouse message so modigy the test pointer first
	pt.x-=m_iXoff;

	if(NULL==pdwSubId)
	{
		return CTreeCtrl::HitTest(pt, pFlags);
	}

	HTREEITEM hItem=MCTHitTest(&pt, pdwSubId);
	if(0==*pdwSubId)
	{
		return CTreeCtrl::HitTest(pt, pFlags);
	}

	return hItem;
}

// may modify the pt for tree control to the item text centern point
HTREEITEM CMCTreeCtrl::MCTHitTest(CPoint* pcPt, DWORD* pdwSubId, RECT* pSubRc)
{
	ASSERT(NULL!=pdwSubId);
	*pdwSubId=0;

	CPoint cptTst=*pcPt;
	cptTst.x=0;			// change to test point to x=0, so it will not exceed the tree control test range
	HTREEITEM htreeitem=CTreeCtrl::HitTest(cptTst);
	if(NULL==htreeitem)
	{
		return htreeitem;
	}

	for(DWORD i=0, x=0;
		i<m_vcClmn.size();
		x+=m_vcClmn[i].m_wWidth, i++)
	{
		if( (pcPt->x > x) && (pcPt->x < x+m_vcClmn[i].m_wWidth))
		{
			*pdwSubId=i;
			if(NULL!=pSubRc)
			{
				GetItemRect(htreeitem, pSubRc, TRUE);
				// adjust the point for tree control from "item right" to item
				if( (0==i) && (pcPt->x > pSubRc->right) )
				{
					pcPt->x=pSubRc->right-1;
				}
				pSubRc->left=x;
				pSubRc->right=x+m_vcClmn[i].m_wWidth;
			}
			break;
		}
	}

	return htreeitem;
}


//
// mouse related functions
//
void CMCTreeCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{
	// TODO: Add your message handler code here and/or call default
	DWORD dwSubId;
	CRect rcSub;
	HTREEITEM htreeitem=MCTHitTest(&point, &dwSubId, rcSub);
	
	if(0==dwSubId)
	{ // tree control's message, use the default tree control winproc, 
		// for we may modify the point in the case of on column0 and right of item
		//		return CTreeCtrl::OnLButtonUp(nFlags, point);
		UINT uFlage;
		CTreeCtrl::HitTest(point, &uFlage);
		{
			if(!(TVHT_ONITEMLABEL&uFlage))
			{
				CTreeCtrl::DefWindowProc(WM_LBUTTONDOWN, nFlags, MAKELPARAM(point.x, point.y));
				return ;
			}
		}
	}

	if(!(TVIS_SELECTED & GetItemState(htreeitem, TVIS_SELECTED)))
	{  // select this item
//			Select(htreeitem, TVGN_CARET);	// select not highlight the item!
		GetItemRect(htreeitem, rcSub, TRUE);
		CTreeCtrl::DefWindowProc(WM_LBUTTONDOWN, nFlags, MAKELPARAM(rcSub.CenterPoint().x, rcSub.CenterPoint().y));
	}
	else
	{
		SetCapture();
		m_fCaptured=TRUE;
	}
}

void CMCTreeCtrl::OnLButtonUp(UINT nFlags, CPoint point)
{
	// TODO: Add your message handler code here and/or call default
	DWORD dwSubId;
	CRect rcSub;

	if(FALSE==m_fCaptured)
	{
		return;
	}
	m_fCaptured=FALSE;
	ReleaseCapture();

	HTREEITEM htreeitem=MCTHitTest(&point, &dwSubId, rcSub);
	if(NULL==htreeitem)
	{
		return;
	}

	if(0==dwSubId)
	{ // tree control's message, use the default tree control winproc, 
	  // for we may modify the point in the case of on column0 and right of item
//		return CTreeCtrl::OnLButtonUp(nFlags, point);
		UINT Flage;
		CTreeCtrl::HitTest(point, &Flage);
		if(!(TVHT_ONITEM&Flage))
		{
			CTreeCtrl::DefWindowProc(WM_LBUTTONUP, nFlags, MAKELPARAM(point.x, point.y));
			return ;
		}
	}

	if(TVIS_SELECTED & GetItemState(htreeitem, TVIS_SELECTED))
	{
		if(m_vcClmn[dwSubId].m_dwFlag & MCT_CLMN_EDITABLE)
		{
			if(NULL==m_pcEdit)
			{
				m_pEditItem=reinterpret_cast<CMCTreeItem*>(CTreeCtrl::GetItemData(htreeitem));
				m_hEditItem=htreeitem;
				m_wEditSubId=dwSubId;
				BeginEdit(&rcSub);
			}
			else
			{
				if( m_pcEdit->m_hWnd==::GetFocus()) 
				{
					::SetFocus(NULL);
				}

			}
		}
	}
	else
	{  // select this item
//			Select(htreeitem, TVGN_CARET);	// select not highlight the item!
		GetItemRect(htreeitem, rcSub, TRUE);
		rcSub.OffsetRect(m_iXoff, 0);		// for DefWindowProc modify the coordinate
		CTreeCtrl::DefWindowProc(WM_LBUTTONUP, nFlags, MAKELPARAM(rcSub.CenterPoint().x, rcSub.CenterPoint().y));
	}
}

void CMCTreeCtrl::DoCopy()
{
	HWND hwndTop=::GetTopWindow(m_hWnd);
	if(NULL!=hwndTop)
		::SendMessage(hwndTop, WM_COPY, 0, 0);
}

void CMCTreeCtrl::DoPaste()
{
	HWND hwndTop=::GetTopWindow(m_hWnd);
	if(NULL!=hwndTop)
		::SendMessage(hwndTop, WM_PASTE, 0, 0);
}

void CMCTreeCtrl::DoCut()
{
	HWND hwndTop=::GetTopWindow(m_hWnd);
	if(NULL!=hwndTop)
		::SendMessage(hwndTop, WM_CUT, 0, 0);
}

//
// edit subitem
//
void CMCTreeCtrl::BeginEdit(CRect* pcRect)
{
	ASSERT(NULL==m_pcEdit);

	//move the edit window to client's coordinates
	pcRect->OffsetRect(m_iXoff, 0);
	pcRect->InflateRect(0,3);

	m_pcEdit=new CInPlcEdit;
	m_pcEdit->Create( WS_CHILD|WS_BORDER|ES_LEFT|ES_AUTOHSCROLL|WS_VISIBLE|ES_WANTRETURN , *pcRect, this, CMCT_EDIT);
	LPCTSTR lpszString;
	CString strTmp;
	if(0==m_wEditSubId)
	{
		strTmp=GetItemText(m_hEditItem);
		lpszString=strTmp.GetString();
//		m_pcEdit->SetWindowText(GetItemText(m_hEditItem).GetString());
	}
	else
	{
		CString* pcStr =&m_pEditItem->m_vStr[m_vcClmn[m_wEditSubId].m_wId];
		lpszString=pcStr->GetString();
//		m_pcEdit->SetWindowText(pcStr->GetString()) ;
	}
	m_pcEdit->SetWindowText(lpszString);

	//
	// give a change for parent to modify the edit control
	//
	if(0!=SendEditNotify(TVN_BEGINLABELEDIT, lpszString))
	{
		delete m_pcEdit;
		m_pcEdit=NULL;
		return;
	}

	m_pcEdit->ShowWindow(SW_SHOW);
	m_pcEdit->SetFocus();
}

void CMCTreeCtrl::EndEdit()
{
	ASSERT(NULL!=m_pcEdit);
    
	CString cstrEdit;
	if(FALSE==m_pcEdit->IsCanceled())
	{
		m_pcEdit->GetWindowText(cstrEdit);
		
		if(TRUE==SendEditNotify(TVN_ENDLABELEDIT, cstrEdit))
		{
			if(0==m_wEditSubId)
			{
				SetItemText(m_hEditItem, cstrEdit);
			}
			else
			{
				SetSubItem(m_hEditItem, m_wEditSubId, cstrEdit.GetBuffer());
			}
		}
	}
	else
	{
		SendEditNotify(TVN_ENDLABELEDIT, NULL);
	}

	delete m_pcEdit;
	m_pcEdit=NULL;
}

//todo the string should be editable, change the const property
// send edit notify to parent
LRESULT CMCTreeCtrl::SendEditNotify(UINT message, LPCTSTR lpszString)
{
	NMTVDISPINFO nmInfo;
	nmInfo.hdr.hwndFrom=m_hWnd;
	nmInfo.hdr.idFrom=GetDlgCtrlID();
	nmInfo.hdr.code=message;
	nmInfo.item.mask=TVIF_HANDLE|TVIF_TEXT|MCTVIF_EDITSUBID|TVIF_PARAM;
	nmInfo.item.hItem=m_hEditItem;
	nmInfo.item.iImage=m_wEditSubId;
	nmInfo.item.pszText=const_cast<LPSTR>(lpszString);
	nmInfo.item.cchTextMax=(NULL==lpszString)?0:_tcslen(lpszString);
	nmInfo.item.lParam=m_pEditItem->m_dwPara;
	return ::SendMessage(::GetParent(m_hWnd), WM_NOTIFY,GetDlgCtrlID(), reinterpret_cast<LPARAM>(&nmInfo));
}

BOOL CMCTreeCtrl::GetEditSubId(DWORD* pdwSubId)
{
	if(NULL==m_pcEdit)
	{
		return FALSE;
	}

	*pdwSubId=m_wEditSubId;
	return TRUE;
}

void CMCTreeCtrl::OnEnKillfocus()
{
	// TODO: Add your control notification handler code here
	EndEdit();
}

void CMCTreeCtrl::UpdateSubCtrls()
{
	// disable edit
	if( (NULL!=m_pcEdit) && (m_pcEdit->m_hWnd==::GetFocus()) )
	{
		::SetFocus(NULL);
	}
}

//
// items
//
HTREEITEM CMCTreeCtrl::InsertItem(LPTVINSERTSTRUCT lpInsertStruct, DWORD_PTR dwPara)
{
	TVITEM* pTVItem = &lpInsertStruct->item;

	CMCTreeItem* pMCTItem=new CMCTreeItem(m_dwDimension, dwPara);
	ASSERT(NULL!=pMCTItem);
	pMCTItem->m_dwPara=pTVItem->lParam;

	// set the TVITEM.lParam for our CMCTreeItem
	pTVItem->mask|=TVIF_PARAM;
	pTVItem->lParam=reinterpret_cast<LPARAM>(pMCTItem);

	HTREEITEM hRet=CTreeCtrl::InsertItem(lpInsertStruct);
	if(NULL==hRet)
	{
		delete pMCTItem;
	}
	return hRet;
}

HTREEITEM CMCTreeCtrl::InsertItem( LPCTSTR lpszItem, int nImage, int nSelectedImage, HTREEITEM hParent, HTREEITEM hInsertAfter, DWORD_PTR dwPara)
{
	TVINSERTSTRUCT tviItem;
	tviItem.hParent=hParent;
	tviItem.hInsertAfter=hInsertAfter;
	memset(&tviItem.item, 0, sizeof(tviItem.item));
	tviItem.item.mask=(TVIF_IMAGE|TVIF_SELECTEDIMAGE|TVIF_TEXT);
	tviItem.item.pszText=const_cast<LPTSTR>(lpszItem);
	tviItem.item.iImage=nImage;
	tviItem.item.iSelectedImage=nSelectedImage;

	return InsertItem(&tviItem, dwPara);
}

BOOL CMCTreeCtrl::SetSubItem(HTREEITEM hItem, DWORD dwSubId, TCHAR* pStr)
{
	ASSERT(dwSubId>=1);		// used only for subitems

	BOOL fRet=FALSE;

	CMCTreeItem* pMCTItem=reinterpret_cast<CMCTreeItem*>(CTreeCtrl::GetItemData(hItem));
	ASSERT(NULL!=pMCTItem);

	if(dwSubId<=m_dwDimension)
	{
		pMCTItem->m_vStr[dwSubId]=pStr;
		// redraw this item
		SetItemText(hItem, GetItemText(hItem));
		fRet=TRUE;
	}

	return fRet;
}

DWORD CMCTreeCtrl::SetSubItemS(HTREEITEM hItem, DWORD dwSize, DWORD *pdwSubIds, CString *pcstrS)
{
	DWORD dwInserted=0;

	CMCTreeItem* pMCTItem=reinterpret_cast<CMCTreeItem*>(CTreeCtrl::GetItemData(hItem));
	ASSERT(NULL!=pMCTItem);

	for(; (dwInserted<dwSize) && (pdwSubIds[dwInserted]<=m_dwDimension); dwInserted++)
	{
		ASSERT(pdwSubIds[dwInserted]>=1);		// used only for subitems
		pMCTItem->m_vStr[pdwSubIds[dwInserted]]=pcstrS[dwInserted];
	}

	// redraw this item
	SetItemText(hItem, GetItemText(hItem));
	return dwInserted;
}

BOOL CMCTreeCtrl::GetSubItem(HTREEITEM hItem, DWORD dwSubId, TCHAR* pStr, DWORD* pdwLen) const
{
	ASSERT(dwSubId>=1);		// used only for subitems

	BOOL fRet=FALSE;

	CMCTreeItem* pMCTItem=reinterpret_cast<CMCTreeItem*>(CTreeCtrl::GetItemData(hItem));
	ASSERT(NULL!=pMCTItem);

	if(dwSubId<=m_dwDimension)
	{
		if(*pdwLen>=pMCTItem->m_vStr[dwSubId].GetLength())
		{
			_tccpy(pStr, pMCTItem->m_vStr[dwSubId]);
			fRet=TRUE;
		}
		*pdwLen=pMCTItem->m_vStr[dwSubId].GetLength();
	}

	return fRet;
}

/*
BOOL CMCTreeCtrl::DeleteItem(HTREEITEM hItem)
{
	CMCTreeItem* pMCTItem=reinterpret_cast<CMCTreeItem*>(CTreeCtrl::GetItemData(hItem));
	ASSERT(NULL!=pMCTItem);

	BOOL fRet=CTreeCtrl::DeleteItem(hItem);
	if(FALSE!=fRet)
	{
		delete pMCTItem;
	}
	return fRet;
}
*/

BOOL CMCTreeCtrl::SetItemData(HTREEITEM hItem, DWORD_PTR dwData)
{
	CMCTreeItem* pMCTItem=reinterpret_cast<CMCTreeItem*>(CTreeCtrl::GetItemData(hItem));
	if(NULL!=pMCTItem)
	{
		return FALSE;
	}
	
	pMCTItem->m_dwPara=dwData;
	return CTreeCtrl::SetItemData(hItem, reinterpret_cast<DWORD_PTR>(pMCTItem));
}

DWORD_PTR CMCTreeCtrl::GetItemData(HTREEITEM hItem)
{
	CMCTreeItem* pMCTItem=reinterpret_cast<CMCTreeItem*>(CTreeCtrl::GetItemData(hItem));
	ASSERT(NULL!=pMCTItem);

	return pMCTItem->m_dwPara;
}

//
// column
//

BOOL CMCTreeCtrl::InsertMCTClmn(int nPos, WORD wWidth, WORD wId, DWORD dwFlag)
{
	if(nPos>m_dwDimension)
	{
		TRACE(_T("InsertMCTClmn invalid pos:%d\r\n"), nPos);
		return FALSE;
	}

	m_dwWidth+=wWidth;
	m_vcClmn.insert(m_vcClmn.begin()+nPos, CTreeClmn(wWidth, wId, dwFlag));
	Invalidate();
	return TRUE;
}

BOOL CMCTreeCtrl::DeleteMCTClmn(int nPos)
{
	if(m_vcClmn.erase(m_vcClmn.begin()+nPos)==m_vcClmn.end())
	{
		return FALSE;
	}

	m_dwWidth-=(m_vcClmn[nPos].m_wWidth);
	Invalidate();
	return TRUE;	
}

BOOL CMCTreeCtrl::GetMCTClmnInfo(int nPos, WORD* pwWidth, WORD* pwId, DWORD* pdwFlag)
{
	if(nPos>=m_vcClmn.size())
	{
		TRACE(_T("GetMCTClmnInfo invalid pos:%d\r\n"), nPos);
		return FALSE;
	}
	if(NULL!=pwWidth)
	{
		*pwWidth=m_vcClmn[nPos].m_wWidth;
	}
	if(NULL!=pwId)
	{
		*pwId=m_vcClmn[nPos].m_wId;
	}
	if(NULL!=pdwFlag)
	{
		*pdwFlag=m_vcClmn[nPos].m_dwFlag;
	}
	return TRUE;
}

BOOL CMCTreeCtrl::EditMCTClmn(int nPos, WORD wWidth, WORD wId, DWORD dwFlag)
{
	if(nPos>=m_vcClmn.size())
	{
		TRACE(_T("EditMCTClmn invalid pos:%d\r\n"), nPos);
		return FALSE;
	}

	m_dwWidth-=(m_vcClmn[nPos].m_wWidth);
	m_dwWidth+=wWidth;
	m_vcClmn[nPos].m_wWidth=wWidth;
	m_vcClmn[nPos].m_wId=wId;
	m_vcClmn[nPos].m_dwFlag=dwFlag;
	Invalidate();
	return TRUE;
}

BOOL CMCTreeCtrl::EditMCTClmnWidth(int nPos, WORD wWidth)
{
	if(nPos>=m_vcClmn.size())
	{
		TRACE(_T("EditMCTClmnWidth invalid pos:%d\r\n"), nPos);
		return FALSE;
	}

	m_dwWidth-=(m_vcClmn[nPos].m_wWidth);
	m_dwWidth+=wWidth;
	m_vcClmn[nPos].m_wWidth=wWidth;
	Invalidate();
	return TRUE;
}

DWORD CMCTreeCtrl::CacClmnWdth()
{
	DWORD dwWidth=0;
	for( std::vector<CTreeClmn>::iterator it= m_vcClmn.begin(); it!=m_vcClmn.end(); it++)
	{
		dwWidth+=it->m_wWidth;
	}
	return dwWidth;
}


//
// iterator
//
BOOL CMCTreeCtrl::ForEach(FOREACH_TYPE type, pfnForEachMCTreeItem pfn, VOID* pv, HTREEITEM hItem)
{
	HTREEITEM hChildItem;

	if(FOREACH_TYPE_PREV==type)
	{
		if(hItem!=TVI_ROOT)
		{
			if(FALSE==(*pfn)(pv, hItem))
			{
				return FALSE;
			}
		}
	}
	
	for( hChildItem=GetChildItem(hItem);
		NULL!=hChildItem;
		hChildItem=GetNextSiblingItem(hChildItem))
	{

		if(FALSE==ForEach(type, pfn, pv, hChildItem))
		{
			return FALSE;
		}
	}

	if(type==FOREACH_TYPE_DEEP)
	{
		if(hItem!=TVI_ROOT)
		{
			if(FALSE==(*pfn)(pv, hItem))
			{
				return FALSE;
			}
		}
	}

	return TRUE;
}

BOOL CMCTreeCtrl::ForEachAfter(FOREACH_TYPE type, pfnForEachMCTreeItem pfn, VOID* pv, HTREEITEM hItem, HTREEITEM hItemAfter)
{
	std::vector<HTREEITEM> vectHitem;
	HTREEITEM hitemParent;

	for(hitemParent=GetParentItem(hItemAfter);
		hitemParent!=NULL;
		hitemParent=GetParentItem(hitemParent) )
	{
		vectHitem.push_back(hitemParent);
		if(hitemParent==hItem)
		{
			break;
		}
	}
	if(hitemParent!=hItem)
	{
		return FALSE;
	}

	return ForEachAfter(type, pfn, pv, hItem, &vectHitem);
	
}

BOOL CMCTreeCtrl::ForEachAfter(FOREACH_TYPE type, pfnForEachMCTreeItem pfn, VOID* pv, HTREEITEM hItem, std::vector<HTREEITEM>* pvectHitem)
{
	HTREEITEM hChildItem;
	BOOL fDofn;

	if(0!=pvectHitem->size())
	{
		hChildItem=pvectHitem->back();
		pvectHitem->pop_back();
		fDofn=FALSE;
	}
	else
	{
		hChildItem=GetChildItem(hItem);
		fDofn=TRUE;
	}

	if(FOREACH_TYPE_PREV==type)
	{
		if( (hItem!=TVI_ROOT) && fDofn )
		{
			if(FALSE==(*pfn)(pv, hItem))
			{
				return FALSE;
			}
		}
	}

	for( ;
		NULL!=hChildItem;
		hChildItem=GetNextSiblingItem(hChildItem))
	{

		if(FALSE==ForEachAfter(type, pfn, pv, hChildItem, pvectHitem))
		{
			return FALSE;
		}
	}

	if(type==FOREACH_TYPE_DEEP)
	{
		if( (hItem!=TVI_ROOT) && fDofn )
		{
			if(FALSE==(*pfn)(pv, hItem))
			{
				return FALSE;
			}
		}
	}

	return TRUE;
}

//////////////////////////////////////////////////////
// help functions
//
BOOL CMCTreeCtrl::DeleteSubItems(HTREEITEM hItem)
{
	if (ItemHasChildren(hItem))
	{
		HTREEITEM hNextItem;
		HTREEITEM hChildItem = GetChildItem(hItem);

		while (hChildItem != NULL)
		{
			hNextItem = GetNextItem(hChildItem, TVGN_NEXT);
			if(FALSE==DeleteItem(hChildItem))
			{
				return FALSE;
			}
			hChildItem = hNextItem;
		}
	}

	return TRUE;
}

